#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "inter_code.h"
#include "error.h"
#include "semantic.h"
#include "gp_list.h"
#include "global_config.h"

static int inst_id = 0;



inter_inst_t *new_general_inst(operator_e op, var_t *result, 
                                 var_t *arg1, var_t *arg2)
{
	inter_inst_t *inst = calloc(1, sizeof(inter_inst_t));						 
	if(!inst){
		SYSTEM_FATAL_ERR(MEM_ALLOC_FAILED); // 直接退出程序
	}			
	inst->op = op;
	inst->result = result;
	inst->arg1 = arg1;
	inst->arg2 = arg2;
	inst->inst_id = inst_id++;
	return inst;
}


inter_inst_t *new_fun_inst(operator_e op, fun_t *fun, var_t *result)
{
    assert(fun);
	inter_inst_t *inst = calloc(1, sizeof(inter_inst_t));						 
	if(!inst){
		SYSTEM_FATAL_ERR(MEM_ALLOC_FAILED);
	}		
	inst->inst_id = inst_id++;
	inst->op = op;
	inst->fun = fun;
	inst->result = result;
	return inst;
}


inter_inst_t *new_push_arg_inst(var_t *arg)
{
    assert(arg);
	inter_inst_t *inst = calloc(1, sizeof(inter_inst_t));						 
	if(!inst){
		SYSTEM_FATAL_ERR(MEM_ALLOC_FAILED);
		return NULL;
	}	
	inst->op = IR_OP_ARG;
	inst->arg1 = arg;
	inst->inst_id = inst_id++;
	return inst;
}


inter_inst_t *new_label_inst(const char *user_lb)
{
	inter_inst_t *inst = calloc(1, sizeof(inter_inst_t));						 
	if(!inst){
		SYSTEM_FATAL_ERR(MEM_ALLOC_FAILED);
		return NULL;
	}	
	
	gen_name(inst->label, GEN_NAME_LB_PREFIX, user_lb);
	inst->inst_id = inst_id++;
	return inst;
}



// (条件)跳转指令
inter_inst_t *new_jmp_inst(operator_e op, inter_inst_t *target, 
                                 var_t *arg1, var_t *arg2)
{
    assert(target);
	inter_inst_t *inst = calloc(1, sizeof(inter_inst_t));						 
	if(!inst){
		SYSTEM_FATAL_ERR(MEM_ALLOC_FAILED);
		return NULL;
	}			
	inst->op = op;
	inst->jmp_target = target;
	inst->arg1 = arg1;
	inst->arg2 = arg2;
	inst->inst_id = inst_id++;
	return inst;
}

void print_var_in_inter_code(var_t *var)
{
	if(!var)
		return;
	
	if(var->is_literal) {
		if(var->type == INT){
			printf("%d", var->int_val);
		} else if(var->type == CHAR) {
			if(var->is_arr)
				printf("%s", var->name);
			else
				printf("'%c'", var->char_val);
		}

	}
	else {
		printf("%s", var->name);

	}

}


#define  TO_STRING(x)  print_var_in_inter_code(x)

#define TWO_IR_OP_INST(x) do{\
	TO_STRING(result);\
	printf(" = ");\
	TO_STRING(arg1);\
	printf(" %s ",#x);\
	TO_STRING(arg2);\
}while(0)
	
	//printf("\t\t%s = %s %s %s", result->note, arg1->note, #x, arg2->note);\
//printf("\t\t%s = %s%s", result->note,  #x, arg1->note);\



#define ONE_IR_OP_INST(x) do{\
	TO_STRING(result);\
	printf(" = ");\
	printf("%s",#x);\
	TO_STRING(arg1);\
}while(0)



void inter_inst_to_string(inter_inst_t *inst)
{
	if(!inst)
		return;
	printf("%4d\t", inst->inst_id);
	if(inst->label[0]){
		printf("%s:\n", inst->label);
		return;
	}
	printf("\t");
	var_t *arg1 = inst->arg1;
	var_t *arg2 = inst->arg2;
	var_t *result = inst->result;
	inter_inst_t *tgt = inst->jmp_target;
	
	switch(inst->op)
	{
		case IR_OP_DECL:		
#ifdef DISPLAY_DECL
			printf(HI_GRE);
			printf("decl ");
			TO_STRING(arg1);  
			if(arg1->pointed_by) {
				printf("(*%s)", arg1->pointed_by->name);
			}
					
			if(arg1->init_by_constant) {
				if(IS_CHAR_PTR(arg1)) {
					printf(" = %s", arg1->string_val);
				} else if(TYPE_IS_CHAR(arg1)) {
					if(arg1->char_val)
						printf(" = '%c'", arg1->char_val);
					else 
						printf(" = '\\0'");
				} else {
					printf(" = %d", arg1->int_val);
				}	
				printf("\t\t%s","init");
			}
			printf(CLOSE);
#else
			if(arg1->init_by_constant) {
				printf(HI_GRE);
				if(arg1->pointed_by)
					printf("*%s", arg1->pointed_by->name);
				else
					TO_STRING(arg1); 

				if(IS_CHAR_PTR(arg1)) {
					printf(" = %s", arg1->string_val);
				} else if(TYPE_IS_CHAR(arg1)) {
					if(arg1->char_val)
						printf(" = '%c'", arg1->char_val);
					else 
						printf(" = '\\0'");
				} else {
					printf(" = %d", arg1->int_val);
				}
				printf("\t\t%s","init");
				printf(CLOSE);	

				
			} else {
				printf("\b");   
				return;
			}

#endif
		break;
		
		case IR_OP_FUN_ENTRY:
			printf("ENTRY");
			break;
		
		case IR_OP_FUN_EXIT:
			printf("EXIT");
			break;

		case IR_OP_ASSIGN:
			if(arg2)	
				printf(HI_GRE);

			TO_STRING(result);
			printf(" = ");
			TO_STRING(arg1);
			
			if(arg2) {				
				printf("\t\t%s", (char *)arg2);
				printf(CLOSE);
			}
			break;
			
		case IR_OP_ADD:
			TWO_IR_OP_INST(+);
			break;
				
		case IR_OP_SUB:
			TWO_IR_OP_INST(-);
			break;
				
		case IR_OP_MUL:
			TWO_IR_OP_INST(*);
			break;
				
		case IR_OP_DIV:
			TWO_IR_OP_INST(/);
			break;
				
		case IR_OP_MOD:
			TWO_IR_OP_INST(%);
			break;

		case IR_OP_NEG:
			ONE_IR_OP_INST(-);
			break;
		case IR_OP_GT:
			TWO_IR_OP_INST(>);
			break;
		case IR_OP_GE:
			TWO_IR_OP_INST(>=);
			break;
		
		case IR_OP_LT:
			TWO_IR_OP_INST(<);
			break;
		case IR_OP_LE:
			TWO_IR_OP_INST(<=);
			break;
		case IR_OP_EQU:
			TWO_IR_OP_INST(==);
			break;

		case IR_OP_NEQU:
			TWO_IR_OP_INST(!=);
			break;

		case IR_OP_NOT:
			ONE_IR_OP_INST(!);
			break;
		
		case IR_OP_AND:
			TWO_IR_OP_INST(&&);
			break;
			
		case IR_OP_OR:
			TWO_IR_OP_INST(||);
			break;

		case IR_OP_JMP:
			assert(tgt);
			printf("goto %s", tgt->label);
			break;

		case IR_OP_JT:
			assert(tgt);
			printf("if(");
			TO_STRING(arg1);
			printf(") goto %s", tgt->label);
			break;

		case IR_OP_JF:
			assert(tgt);
			printf("if(!");
			TO_STRING(arg1);
			printf(") goto %s", tgt->label);
			break;

		case IR_OP_JE:
			assert(tgt);
			printf("if(");
			TO_STRING(arg1);
			printf(" == ");
			TO_STRING(arg2);
			printf(") goto %s",tgt->label);
			break;

		case IR_OP_JNE: // 目前没有用到
			assert(tgt);
			printf("if(");
			TO_STRING(arg1);
			printf(" != ");
			TO_STRING(arg2);
			printf(") goto %s",tgt->label);
			break;

		case IR_OP_ARG:
			printf("arg ");
			TO_STRING(arg1);
			break;

		case IR_OP_PROC:
			printf("%s()", inst->fun->name);
			break;

		case IR_OP_CALL:
			TO_STRING(result);
			printf(" = %s()", inst->fun->name);
			break;

		case IR_OP_RET:
			assert(tgt);	
			printf("return goto %s",tgt->label);
			break;

		case IR_OP_RET_VAL:
			printf("return ");
			TO_STRING(arg1);
			assert(tgt);
			printf(" goto %s",tgt->label);
			break;	

		case IR_OP_LEA:
			TO_STRING(result);
			printf(" = ");
			printf("&");
			TO_STRING(arg1);
			break;

		case IR_OP_STORE:
			if(arg2)
				printf(HI_GRE);

			printf("*");
			TO_STRING(arg1);
			printf(" = ");
			TO_STRING(result);

			if(arg2) {
				printf("\t\t%s", (char *)arg2);
				printf(CLOSE);
			}
				
			break;
			break;

		case IR_OP_LOAD:
			if(arg2)
				printf(HI_GRE);
			TO_STRING(result);
			printf(" = ");
			printf("*");
			TO_STRING(arg1);

			if(arg2) {
				printf("\t\t%s", (char *)arg2);
				printf(CLOSE);
			}
			break;	

		default:
			break;		
	}
	printf("\n");
}

void inter_inst_to_dot_string(inter_inst_t *inst)
{
	if(!inst)
		return;
	var_t *arg1 = inst->arg1;
	var_t *arg2 = inst->arg2;
	var_t *result = inst->result;
	inter_inst_t *tgt = inst->jmp_target;
	//  <FONT FACE="Arial" COLOR="darkblue" POINT-SIZE="14"><B>Block 0</B></FONT>

	// 没有初始值的 decl,不输出
	if(!inst->label[0] && inst->op == IR_OP_DECL && !arg1->init_by_constant)
		return;
	if(inst->op != IR_OP_FUN_EXIT && inst->op != IR_OP_FUN_ENTRY) // 入口和出口不打印指令的编号，这样更好看
		printf("(%d)  ", inst->inst_id);
	
	if(inst->label[0]){
		printf("%s:\\l", inst->label);  // \l 表示左对齐
		return;
	}

	switch(inst->op)
	{
		case IR_OP_DECL:		
			if(arg1->init_by_constant) {
				if(arg1->pointed_by)
					printf("*%s", arg1->pointed_by->name);
				else
					TO_STRING(arg1); 

				if(IS_CHAR_PTR(arg1)) {
					printf(" = %s", arg1->string_val);
				} else if(TYPE_IS_CHAR(arg1)) {
					if(arg1->char_val)
						printf(" = '%c'", arg1->char_val);
					else 
						printf(" = '\\0'");
				} else {
					printf(" = %d", arg1->int_val);
				}
				
			} else {
				printf("\b\b\b\b");
				return;
			}

		break;
		
		case IR_OP_FUN_ENTRY:
			printf("ENTRY");
			break;
		
		case IR_OP_FUN_EXIT:
			printf("EXIT");
			break;

		case IR_OP_ASSIGN:
		
			TO_STRING(result);
			printf(" = ");
			TO_STRING(arg1);

			break;
			
		case IR_OP_ADD:
			TWO_IR_OP_INST(+);
			break;
				
		case IR_OP_SUB:
			TWO_IR_OP_INST(-);
			break;
				
		case IR_OP_MUL:
			TWO_IR_OP_INST(*);
			break;
				
		case IR_OP_DIV:
			TWO_IR_OP_INST(/);
			break;
				
		case IR_OP_MOD:
			TWO_IR_OP_INST(%);
			break;

		case IR_OP_NEG:
			ONE_IR_OP_INST(-);
			break;
		case IR_OP_GT:
			TWO_IR_OP_INST(>);
			break;
		case IR_OP_GE:
			TWO_IR_OP_INST(>=);
			break;
		
		case IR_OP_LT:
			TWO_IR_OP_INST(<);
			break;
		case IR_OP_LE:
			TWO_IR_OP_INST(<=);
			break;
		case IR_OP_EQU:
			TWO_IR_OP_INST(==);
			break;

		case IR_OP_NEQU:
			TWO_IR_OP_INST(!=);
			break;

		case IR_OP_NOT:
			ONE_IR_OP_INST(!);
			break;
		
		case IR_OP_AND:
			TWO_IR_OP_INST(&&);
			break;
			
		case IR_OP_OR:
			TWO_IR_OP_INST(||);
			break;

		case IR_OP_JMP:
			assert(tgt);
			printf("goto %s", tgt->label);
			break;

		case IR_OP_JT:
			assert(tgt);
			printf("if(");
			TO_STRING(arg1);
			printf(") goto %s", tgt->label);
			break;

		case IR_OP_JF:
			assert(tgt);
			printf("if(!");
			TO_STRING(arg1);
			printf(") goto %s", tgt->label);
			break;

		case IR_OP_JE:
			assert(tgt);
			printf("if(");
			TO_STRING(arg1);
			printf(" == ");
			TO_STRING(arg2);
			printf(") goto %s",tgt->label);
			break;

		case IR_OP_JNE: // 目前没有用到
			assert(tgt);
			printf("if(");
			TO_STRING(arg1);
			printf(" != ");
			TO_STRING(arg2);
			printf(") goto %s",tgt->label);
			break;

		case IR_OP_ARG:
			printf("arg ");
			TO_STRING(arg1);
			break;

		case IR_OP_PROC:
			printf("%s()", inst->fun->name);
			break;

		case IR_OP_CALL:
			TO_STRING(result);
			printf(" = %s()", inst->fun->name);
			break;

		case IR_OP_RET:
			assert(tgt);	
			printf("return goto %s",tgt->label);
			break;

		case IR_OP_RET_VAL:
			printf("return ");
			TO_STRING(arg1);
			assert(tgt);
			printf(" goto %s",tgt->label);
			break;	

		case IR_OP_LEA:
			TO_STRING(result);
			printf(" = ");
			printf("&");
			TO_STRING(arg1);
			break;

		case IR_OP_STORE:

			printf("*");
			TO_STRING(arg1);
			printf(" = ");
			TO_STRING(result);
				
			break;
			break;

		case IR_OP_LOAD:
			TO_STRING(result);
			printf(" = ");
			printf("*");
			TO_STRING(arg1);

			break;	

		default:
			break;		
	}
	printf("\\l");	
}

void print_inter_code(struct list_head *head)
{
	if(!head)
		return;
		
	inter_inst_t *pos;
	list_for_each_entry(pos, head, entry){
		inter_inst_to_string(pos);
	}
	printf("\n");
}


// 判断是不是无条件跳转指令	
bool inter_inst_is_jmp(inter_inst_t *inst)
{
	operator_e op = inst->op;
	return op == IR_OP_JMP || op == IR_OP_RET || op == IR_OP_RET_VAL;
}


// 判断是不是条件跳转指令
bool inter_inst_is_cond_jmp(inter_inst_t *inst)
{
	operator_e op = inst->op;
	/* IR_OP_JF, IR_OP_JT, IR_OP_JE, IR_OP_JNE */
	return op >= IR_OP_JF && op <= IR_OP_JNE;
}



/*
	替换表达式指令信息
	常量传播使用
*/
void replace_inst(inter_inst_t *inst, operator_e op, 
			var_t *res, var_t *arg1, var_t *arg2)
{
	inst->op = op;
	inst->result = res;
	inst->arg1 = arg1;
	inst->arg2 = arg2;
}




void print_optimized_code(struct list_head *gp_list_head)
{
	if(!gp_list_head)
		return;
	gp_node_t *pos;
	list_for_each_entry(pos, gp_list_head, entry){
		inter_inst_to_string((inter_inst_t *)pos->addr);
	}	
}

